/*
 Taylor Seale, Justin Barlett, Ka Hin Lee
 Computer Networks - Project 2
 Multithreaded Thermometer Server
 
 Client portion of Project 2
 
 Client is to be run by the cron service. The client reads the information regarding the sensor(s) from the conf
 file and then reads the current value of the sensor(s). It then connects to the server and sends this information.
 If there is an error it writes to an error log.
*/

#include <stdio.h>		
#include <string.h>		
#include <stdlib.h>		
#include <sys/socket.h>
#include <errno.h>		
#include <netdb.h>		
#include <arpa/inet.h>
#include <unistd.h>		
// for reading temps
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <strings.h>
// for timestamp
#include <sys/time.h>
#include <time.h>


// Helper Functions

// Function to write to error log
void printErrorMessage(char *);
// Function to return a timestamp of the current time
char * getTimeStamp(void);
// Function to convert Celsius to Fahrenheit
float CtoF(float C){return (C*9.0/5.0)+32;}
// Function to convert hostname to ip address
char *hostname_to_ip (char *);

// Structs
struct client_data
{
	char host_name[32];
	int num_therms;
	int sensor_num;
	double sensor_data;
	double low_val;
	double high_val;
	char timestamp[32];
	int action;
};

struct packet
{
	unsigned char measurements;
	unsigned char counter;
	int16_t measurement0;
	int16_t measurement1;
	int16_t measurement2; 
};

int main (int argc, char **argv)
{
	sleep(10); // to handle cron issues, can be removed if cron issues fixed
	
	// establish necessary variables here
	int sockfd, n;		// socket and received buffer length

	// ensure correct number of arguments passed
	if (argc != 3)
	{
		printErrorMessage("Incorrect Arguments - usage: client <host> <port>\n");
		exit (1);
	}
	
	// create socket and check for errors!
	if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) == -1)
	{
		printErrorMessage("Error creating socket\n");
		exit (1);
	}
	
	// to convert host name (returns original IP or hostname converted to IP)
	char *host = hostname_to_ip (argv[1]);
	
	// set up all the network stuff
	struct sockaddr_in servaddr;
	bzero (&servaddr, sizeof (servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = inet_addr (host);
	servaddr.sin_port = htons (atoi (argv[2]));
	
	// connect to the server and check for errors!
	if (connect (sockfd, (struct sockaddr *) &servaddr, sizeof (servaddr)) == -1)
	{
		printErrorMessage("Error creating a connection with the server\n");
		exit (1);
	}
	
	// read the conf file
	FILE * fp;
	char line[10];
	int numSensors=0;
	double sensorOneLow=0;
	double sensorOneHigh=0;
	double sensorTwoLow=0;
	double sensorTwoHigh=0;
	fp = fopen ("/etc/t_client/client.conf", "rt"); //open file for reading
	
	// make sure file was properly opened
	if (fp == NULL)
	{
		printErrorMessage("Error opening configuration file\n");
	}
	int pass=0;	// used to determine where to store information
	// read information and store it properly
	while (fgets(line,10,fp) != NULL)
	{
		if (pass == 0)
		{
			numSensors=atoi(line);
		}
		if (pass == 1)
		{
			char * pch;
			pch = strtok (line, " ");
			int temp2=0;
			while (pch != NULL)
			{
				if (temp2==0)
				{
					sensorOneLow=atof(pch);
				}
				else
				{
					sensorOneHigh=atof(pch);
				}
				pch = strtok(NULL, " ");
				temp2++;
			}
		}
		// not sure why this is three? but it works
		if (pass == 3)
		{
			//printf ("LINE temp 2: %s\n",line);
			char * pch2;
			pch2 = strtok (line, " ");
			int temp2=0;
			while (pch2 != NULL)
			{
				if (temp2==0)
				{
					sensorTwoLow=atof(pch2);
				}
				else
				{
					sensorTwoHigh=atof(pch2);
				}
				pch2 = strtok(NULL, " ");
				temp2++;
			}
		}
		pass++;
	}
	
	// print info to ensure it is correct
	/*
	printf("Num Sensors: %d\n",numSensors);
	printf("Sensor1 Low: %f\n",sensorOneLow);
	printf("Sensor1 High: %f\n",sensorOneHigh);
	printf("Sensor2 Low: %f\n",sensorTwoLow);
	printf("Sensor2 High: %f\n",sensorTwoHigh);
	*/
	
	
	//get the current value of sensors
	//The below portion of Code was modified from Professor Freeland, whom modified it from the people listed below
	   /* Go! Temp reader
	* written by Jeff Sadowski <jeff.sadowski@gmail.com>
	* with information gathered from David L. Vernier
	* and Greg KH This Program is Under the terms of the 
	* GPL http://www.gnu.org/copyleft/gpl.html
	* Any questions feel free to email me :-)
	*/
	/* This is close to the structure Professr Freeland found in Greg's Code */
	char *fileName="/dev/gotemp";
	char *fileName2="/dev/gotemp2";
	struct stat buf, buf2;
	struct packet temp, temp2;
	
	/* Professor Freeland got this number from the GoIO_SDK and it matched 
	   what David L. Vernier got from his Engineer */	
	float conversion=0.0078125;
	int fd, fd2;
	
	if(stat( fileName, &buf ))
	{
		if(mknod(fileName,S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP |S_IWGRP|S_IROTH|S_IWOTH,makedev(180,176)))
		{
			char errorBuffer[100];
			sprintf (errorBuffer,"Cannot create device %s need to be root\n",fileName);
			printErrorMessage(errorBuffer);
			exit(1);
		}
	}
	
	if(stat( fileName2, &buf2 ))
	{
		if(mknod(fileName2,S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP |S_IWGRP|S_IROTH|S_IWOTH,makedev(180,177)))
		{
			char errorBuffer[100];
			sprintf (errorBuffer,"Cannot create device %s need to be root\n",fileName2);
			printErrorMessage(errorBuffer);
			exit(1);
		}
	}
	
	/* If cannot open, check permissions on dev, and see if it is plugged in */
	
	if((fd=open(fileName,O_RDONLY))==-1)
	{
		char errorBuffer[100];
		sprintf (errorBuffer,"Could not read %s\n",fileName);
		printErrorMessage(errorBuffer);	
		exit(1);
	}
	if((fd2=open(fileName2,O_RDONLY))==-1)
	{
		char errorBuffer[100];
		sprintf (errorBuffer,"Could not read %s\n",fileName2);
		printErrorMessage(errorBuffer);	
		exit(1);
	}
	
	/* if cannot read, check is it plugged in */
	
	if(read(fd,&temp,sizeof(temp))!=8)
	{
		char errorBuffer[100];
		sprintf (errorBuffer,"Error reading %s\n",fileName);
		printErrorMessage(errorBuffer);
		exit(1);
	}
	
	if(read(fd2,&temp2,sizeof(temp))!=8)
	{
		char errorBuffer[100];
		sprintf (errorBuffer,"Error reading %s\n",fileName2);
		printErrorMessage(errorBuffer);
		exit(1);
	}
	
	close(fd);
	close(fd2);
	
	double sensorOneTemp;
	double sensorTwoTemp;
	sensorOneTemp=CtoF(((float)temp.measurement0)*conversion);
	sensorTwoTemp=CtoF(((float)temp2.measurement0)*conversion);
	
	//print sensor temps
	//printf("Sensor 1 Temp: %3.2f\nSensor 2 Temp: %3.2f\n",sensorOneTemp,sensorTwoTemp);
	     
	// get timestamp
	char *timestamp = (char *)malloc(sizeof(char)*32);
	memset(&timestamp[0],0,sizeof(timestamp));
	timestamp = getTimeStamp();
	
	// transfer sensor data
	struct client_data sensorOne;
	bzero(&sensorOne,sizeof(sensorOne));
	char hostname[32];
	gethostname(hostname,sizeof(hostname));
	strncpy(sensorOne.host_name,hostname, sizeof(sensorOne.host_name));
	sensorOne.num_therms=numSensors;
	sensorOne.sensor_num=0; //***** SENSOR ONE IS 0 for sensor num *******
	sensorOne.sensor_data=sensorOneTemp;
	sensorOne.low_val=sensorOneLow;
	sensorOne.high_val=sensorOneHigh;
	strncpy(sensorOne.timestamp,timestamp, sizeof(sensorOne.timestamp));
	sensorOne.action=0;
	
	// send and error check!
	n=write(sockfd, (struct client_data*)&sensorOne, sizeof (sensorOne));
	if (n<0)
	{
		printErrorMessage("Sending data for sensor 0 failed\n");
		exit(1);
	}
	//printf("Sensor 1 Send Complete... Bytes Sent: %d\n",n);

	// send data for second sensor if it exists
	if (numSensors > 1)
	{
		struct client_data sensorTwo;
		bzero(&sensorTwo,sizeof(sensorTwo));
		char hostname[32];
		gethostname(hostname,sizeof(hostname));
		strncpy(sensorTwo.host_name,hostname, sizeof(sensorTwo.host_name));
		sensorTwo.num_therms=numSensors;
		sensorTwo.sensor_num=1;	//***** SENSOR TWO IS 1 for sensor num *******
		sensorTwo.sensor_data=sensorTwoTemp;
		sensorTwo.low_val=sensorTwoLow;
		sensorTwo.high_val=sensorTwoHigh;
		strncpy(sensorTwo.timestamp,timestamp, sizeof(sensorTwo.timestamp));
		sensorOne.action=0;
		
		// send and error check!
		n=write(sockfd, (struct client_data*)&sensorTwo, sizeof (sensorTwo));
		if (n<0)
		{
			printErrorMessage("Sending data for sensor 1 failed\n");
			exit(1);
		}
		//printf("Sensor 2 Send Complete... Bytes Sent: %d\n",n);
	}
	
	close (sockfd);
	return 0;
}


// if a hostname is given, convert it to an IP address
// if an IP address is given, that IP address is returned
char * hostname_to_ip (char *host)
{
	struct hostent *h;
	h = gethostbyname (host);
	host = inet_ntoa (*((struct in_addr *) h->h_addr));
	return host;
}

// print error message to file with timestamp
void printErrorMessage(char *error)
{
	//get timestamp
	char *timestamp = (char *)malloc(sizeof(char)*32);
	memset(&timestamp[0],0,sizeof(timestamp));
	timestamp = getTimeStamp();

	FILE *fp = fopen("/var/log/therm/error/g06_error_log", "a");
	if (fp == NULL)
	{
		printf("Error opening file!\n");
	}
	
	fprintf(fp, "%s: ", timestamp);
	fprintf(fp, "%s", error);
	fclose(fp);
}

//return a timestamp for the current time
char * getTimeStamp()
{
	//get timestamp
	char *timestamp = (char *)malloc(sizeof(char)*32);
	memset(&timestamp[0],0,sizeof(timestamp));
	time_t ltime;
	ltime=time(NULL);
	struct tm *tm;
	tm=localtime(&ltime);
	//get ms
	struct timeval tv;
	gettimeofday(&tv,NULL);
	
	sprintf(timestamp,"%04d %02d %02d %02d %02d",tm->tm_year+1900,tm->tm_mon+1,tm->tm_mday,tm->tm_hour,tm->tm_min);
	//sprintf(timestamp,"%02d:%02d:%02d.%06d", tm->tm_hour,tm->tm_min, tm->tm_sec,(int)tv.tv_usec);
	return timestamp;
}
